////////////////////////////////////////////////////////////////////////////////
// Multi threaded benchmark example
//
////////////////////////////////////////////////////////////////////////////////
//TERMS OF USAGE
//----------------------------------------------------------------------
//
//	Permission to use, copy, modify and distribute this software and
//	its documentation for any purpose is hereby granted without fee,
//	provided that due acknowledgments to the authors are provided and
//	this permission notice appears in all copies of the software.
//	The software is provided "as is". There is no warranty of any kind.
//
//Authors:
//	Maurice Herlihy
//	Brown University
//      and
//	Nir Shavit
//	Tel-Aviv University
//      and
//	Moran Tzafrir
//	Tel-Aviv University
//
// Date: Dec 2, 2008.
//
////////////////////////////////////////////////////////////////////////////////
// Programmer : Moran Tzafrir (MoranTza@gmail.com)
//
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//INCLUDE DIRECTIVES
////////////////////////////////////////////////////////////////////////////////
#include "iostream"
#include "cpp_framework.h"
#include "LockFreeList.h"
#include "LockFreeQueue.h"
#include "LazyList.h"
#include "SimpleLinear.h"

////////////////////////////////////////////////////////////////////////////////
//CONSTS
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//INNER CLASSES
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//GLOBALS
////////////////////////////////////////////////////////////////////////////////
static const int gNumThreads = 128;
static const int gTime = 10;

CMDR::Random	gRand;
int				gTotalRandNum;
int*				gRandNumAry;
unsigned int*	gThreadResultAry;

CMDR::VolatileType<int>					gIsStopThreads(0);
CMDR::AtomicInteger						gThreadCounter(0);	
CMDR::AtomicInteger						gEndThreadCounter(0);	
CMDR::AtomicReference<int>				gAtomicThreadRef;
CMDR::AtomicMarkableReference<int>  gAtomicMarkThreadRef;
CMDR::AtomicStampedReference<int>	gAtomicStampedThreadRef;
CMDR::Condition							gStartCondition;

CMDR::ReentrantLock						gReentrantLock;
CMDR::Semaphore							gSemaphore(gNumThreads-1);

int*											gThreadIntAry;

tick_t							gStartTime;
tick_t							gEndTime;

class BenchmarkThread;
BenchmarkThread*			_gThreads;

#define MAX_SIMPLE_LINEAR	(8*1024)

LockFreeList<int>			_gList;
LockFreeQueue<int>		_gQueue;
LazyList<int>				_gLazy(gNumThreads);
SimpleLinear<int, 0>		_gSimpleLinear(MAX_SIMPLE_LINEAR);

////////////////////////////////////////////////////////////////////////////////
//TYPEDEFS
////////////////////////////////////////////////////////////////////////////////
class BenchmarkThread: public CMDR::Thread {
public:
	int _threadNo;
	static CMDR::AtomicInteger _threadCounter;

	BenchmarkThread () : Thread() {
		_threadNo = _threadCounter.getAndIncrement();
	}

	virtual void run() {
		//save start benchmark time 
		const int start_counter = gThreadCounter.getAndIncrement();
		if(start_counter == (gNumThreads-1)) {
			gStartTime = CMDR::System::currentTimeMillis();
		}
		while(gThreadCounter.get() < (gNumThreads-1)) {;}

		printf("s%d\n",start_counter);

		int add_item=1;
		while (true) {

			_gList.add(add_item+10000);
			_gList.contains(add_item);
			_gList.remove(add_item);
			_gList.contains(add_item);

			_gQueue.enq(add_item);
			_gQueue.peek();
			_gQueue.deq();
			_gQueue.peek();

			_gLazy.add(add_item+10000, start_counter);
			_gLazy.contains(add_item+10000, start_counter);
			_gLazy.remove(add_item, start_counter);
			_gLazy.contains(add_item, start_counter);

			const int tmp_value = (add_item%(MAX_SIMPLE_LINEAR-1))+1;
			const int tmp_value2 = ((add_item+10000)%(MAX_SIMPLE_LINEAR-1))+1;
			_gSimpleLinear.add( tmp_value, tmp_value );
			_gSimpleLinear.peek();
			_gSimpleLinear.removeMin();
			_gSimpleLinear.peek();

			//check if need to end benchmark
			if (0 != gIsStopThreads.get()) {
				break;
			}

			++add_item;
		}
		//} while(true);
		printf("e%d\n",start_counter);

		//save end benchmark time
		const int end_counter = gEndThreadCounter.getAndIncrement();
		if(end_counter == (gNumThreads-1)) {
			gEndTime = CMDR::System::currentTimeMillis();
		}
	}
};

CMDR::AtomicInteger BenchmarkThread::_threadCounter(0);

////////////////////////////////////////////////////////////////////////////////
//MAIN
////////////////////////////////////////////////////////////////////////////////

int main(int argc, char **argv) {
	//
	CMDR::Thread::set_concurency_level(gNumThreads);

	//create random variables ..............................................
	gThreadIntAry = new int[gNumThreads];
	for(int iThread=0; iThread<gNumThreads; ++iThread) {
		gThreadIntAry[iThread] = gRand.nextInt(1024);
	}

	for (int iTry=0; iTry<10; ++iTry) {
		//create benchmark threads .............................................
		printf("    START creating threads.\n"); fflush(stdout);
		_gThreads = new BenchmarkThread[gNumThreads];
		gThreadResultAry = new unsigned int[gNumThreads];
		CMDR::Thread::sleep(1*1000);
		printf("    END   creating threads.\n"); fflush(stdout);
		printf("\n\n"); fflush(stdout);

		//start the benchmark threads ..........................................
		printf("    START threads.\n"); fflush(stdout);
		for(int iThread = 0; iThread < gNumThreads; ++iThread) {
			_gThreads[iThread].start();
		}
		printf("    END   threads.\n"); fflush(stdout);
		printf("\n"); fflush(stdout);

		//Wait the throughput time, and then signal the threads to terminate ...
		printf("    STARTING test.\n"); fflush(stdout);
		CMDR::Thread::sleep(gTime * 1000);
		gIsStopThreads.set(1);

		//join the threads .....................................................
		for(int iThread = 0; iThread < gNumThreads; ++iThread) {
			//printf("    try join %d\n", iThread); fflush(stdout);
			_gThreads[iThread].join();
		}
		printf("    ALL threads terminated. %u\n", (int)((gEndTime - gStartTime)/1000) ); fflush(stdout);
		delete [] _gThreads;
		delete [] gThreadResultAry;
	}
	



	return 0;
}
